package projet;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import javax.swing.JComboBox;
import javax.swing.JTextArea;

import exceptions.*;


public class Workshop {
       
        // attributs
        private String name;
        private Float pricePerHour;
        /* ensemble des pieces que l'atelier peut fournir, achetees ou produites */
        private HashSet<Piece> availablePieces ;
        /* ensemble des operations que l'atelier peut realiser */
        private HashSet<Operation> gamme ;
        /* ensemble des fournisseurs aupres desquels l'atelier peut acheter des pieces */
        private HashSet<Provider> fournisseurs ;
       
        // constructeurs
        /**
         * Constructeur sans arguments pour le Parseur XML.
         */
        public Workshop () {
                availablePieces=new HashSet<Piece>();
                gamme=new HashSet<Operation>();
                fournisseurs=new HashSet<Provider>()  ;
        }
       
       
        /**
         * Constructeur avec argument pour nous
         */
        public Workshop (String name, Float pricePerHour) {
               
                availablePieces=new HashSet<Piece>() ;
                gamme=new HashSet<Operation>() ;
                fournisseurs=new HashSet<Provider>() ;
                this.name=name ;
                this.pricePerHour=pricePerHour ;
               
        }
       
       
        // methodes
       
        /**
         * @param name the name to set
         */
        public void setName(String name) {
                this.name = name;
        }


        /**
         * @param pricePerHour the pricePerHour to set
         */
        public void setPricePerHour(Float pricePerHour) {
                this.pricePerHour = pricePerHour;
        }
       
        /**
         * Ajouter un nouveau fournisseur
         * @param p le fournisseur
         */
        public void addProvider(Provider p) {
                this.fournisseurs.add(p) ;
        }
       
        /**
         * Ajouter une nouvelle piece produite
         * @param p la piece
         */
        public void addProducedPiece(ProducedPiece p) throws PPCheckMontageException{
                if (p.checkMontage()){
                this.availablePieces.add(p) ;
                }
                else{
                        throw new PPCheckMontageException();
                }
        }
       
        /**
         * Ajouter une nouvelle piece achetee
         * @param p la piece
         */
        public void addPurchasedPiece(PurchasedPiece p) {
                this.availablePieces.add(p) ;
        }
       
        /**
         * Ajouter une nouvelle operation
         * @param p la piece
         */
        public void addOperation(Operation o) {
                this.gamme.add(o) ;
        }
       
        /**
         * Pour affichage nom dans titre
         */
        public String getName(){
                return name;
        }
       
        /**
         * Ajouter les noms des pieces dans une comboBox, dans l'ordre alphabetique.
         * @param b la comboBox
         */
        public void fillComboBox(JComboBox b) {
                /* Creer un iterateur pour parcourir les pieces */
                Iterator<Piece> iter = availablePieces.iterator() ;
                Piece piece ;
                /* Ajouter les nom à la comboBox */
                /* On doit eviter les doublons et trier les noms */
                while(iter.hasNext()) {
                        piece = iter.next() ;
                        b.removeItem(piece.getLabel()) ; /* Retirer la piece si elle y est deja, pour eviter les doublons */
                        int i ; boolean added = false ; int compare ;
                        for(i=0 ; i<b.getItemCount() ; i++) {
                                compare = piece.getLabel().compareToIgnoreCase((String) b.getItemAt(i)) ;
                                if(compare < 0 ) { /* Cas où la chaine à plus "petite" */
                                        b.insertItemAt(piece.getLabel(),i) ; /* Inserer la chaine la chaine */
                                        added = true ;
                                        break ;
                                } else if(compare == 0) { /* Cas ou la chaine et deja dans la comboBox */
                                        added = true ;
                                        break ;
                                }
                        }
                        /* Sortie du for, si i==b.getItemCount, c'est que l'élément est à ajouter en fin de liste */
                        if(!added) {
                                b.addItem(piece.getLabel()) ;
                        }
                }
        }
       
        /**
         * Ajout des infos du workshop au JPanel d'affichage, une fois celui-ci charge
         * @param p le JPanel
         */
        public void PrintOnPanel(JTextArea content){
                String Text="";
               
                /* ajout des informations */
                        /* celles du workshop */
                Text=Text+"Workshop: "+name+"\n\n\tPrix Horaire: "+pricePerHour+"\n\tProviders:\n";
                        /* celles des providers */
                Iterator<Provider> itf=fournisseurs.iterator();
                for (;itf.hasNext();)
                        Text=Text+""+itf.next().PrintOnPanel();
                        /* celles des pieces */
                Text=Text+"\n\tPieces:\n";
                Iterator<Piece> itp=availablePieces.iterator();
                for (;itp.hasNext();)
                        Text=Text+""+itp.next().PrintOnPanel();
                        /* celles des operations */
                Text=Text+"\n\tOperations:\n";
                Iterator<Operation> ito=gamme.iterator();
                for (;ito.hasNext();)
                        Text=Text+""+ito.next().PrintOnPanel();
               
                /* ajout Text a content */
                content.setText(Text);
                /* liberation iterators au cas ou */
                itf=null;
                itp=null;
                ito=null;
        }
       
       
        /**
         * Calcul de la chaine à écrire dans un fichier xml pour sauver l'atelier.
         * @return la chaine à écrire.
         */
       
        public String xml(){
                Iterator <Piece> it1= availablePieces.iterator();
                Iterator <Operation> it2= gamme.iterator();
                Iterator <Provider> it3= fournisseurs.iterator();
               
                StringBuffer stbuf = new StringBuffer("<Workshop>\n");
                stbuf.append(" <name>"+name+"</name>\n");
                stbuf.append(" <pricePerHour>"+pricePerHour+"</pricePerHour>\n");
                while(it1.hasNext())
                        ((Piece)(it1.next())).xml(stbuf);
                while(it3.hasNext())
                        ((Provider)(it3.next())).xml(stbuf);
                while(it2.hasNext())
                        ((Operation)(it2.next())).xml(stbuf);
                stbuf.append("</Workshop>");
                return ""+stbuf;
        }


       
        /**
         * Calcul recursivement la procédure la plus économique.
         * @param label le libellé de la piece
         * @param tabs le decalage a appliquer pour la mise en forme des instruction
         * @param dejaVu liste des pieces déjà "en construction", pour éviter les boucles
         * @return la procédure la plus économique
         */
        public Process minPrice(String label, String tabs, ArrayList<String> dejaVu) throws InfiniteLoopException {
        		
        		/* Ajout de la piece à la liste des pieces deja vues */
        		dejaVu.add(label) ;
                
                /* Initialisation de la procédure à suivre */
                Process minProc = new Process(-1,"") ;
                Piece piece ;
               
                /* Recherche, parmis les pieces, de celles qui correspondes au label */
                Iterator<Piece> iterPieces = availablePieces.iterator() ;
                while(iterPieces.hasNext()) {
                        piece=iterPieces.next() ;
                        /* Quand une piece adéquate est trouvée */
                        if(piece.getLabel().compareToIgnoreCase(label)==0) {
                               
                                /* Cas où la piece est acheté. */
                                if (piece instanceof PurchasedPiece)  {
                                        /* Si la piece est trouvé pour la première fois ou moins chère, on la choisie */
                                        if(minProc.price==-1 || ((PurchasedPiece) piece).getPrice()<minProc.price) {
                                                minProc.price = ((PurchasedPiece)piece).getPrice() ;
                                                minProc.instructions = tabs+"Acheter la piece "+label+" à "+((PurchasedPiece)piece).getProviderName()+".\n" ;
                                        }
                                }
                               
                                /* Cas où la pièce est fabriquée */
                                if (piece instanceof ProducedPiece)  {
                                        /* Il va falloir calculer recursivement le prix de fabrication de cette piece */
                                        Process producedPieceProc = new Process(0,tabs+"Fabriquer la piece "+((ProducedPiece) piece).description()+"\n") ;
                                        /* On commence par parcourir les noms des operation neccéssaire à la fabrication */
                                        Iterator<String> iterOperationName = ((ProducedPiece)piece).iterator() ;
                                        String operationName ;
                                        while(iterOperationName.hasNext()) {
                                                operationName=iterOperationName.next() ;
                                                /* Pour chaque nom, cherche l'opération qui correspond */
                                                /* On parcours les opération disponibles dans cet atelier */
                                                Iterator<Operation> iterOperation = this.gamme.iterator() ;
                                                Operation op ;
                                                while(iterOperation.hasNext()) {
                                                        op=iterOperation.next() ;
                                                        if(op.getName().compareToIgnoreCase(operationName)==0){ /* Quand la bonne operation est trouvée */
                                                        		/* L'écrire dans les instructions */
                                                        		producedPieceProc.instructions+=(tabs+"|\tOperation "+op.description()+"\n") ;
                                                                /* Il faut calculer le prix minimal de chaque pièce néccéssaire à l'opération */
                                                                /* On parcours la liste des pièces de l'opération (un 4e itérateur) */
                                                                Iterator<String> iterNeededPieces = op.iterator() ;
                                                                String neededPiece ;
                                                                while(iterNeededPieces.hasNext()) {
                                                                		/* Pour chaque piece neccessaire, on calcule comment l'obtenir*/
                                                                		/* Puis on ajoute le résultat */
                                                                        neededPiece = iterNeededPieces.next() ;
                                                                        /* Si cette piece est déjàVu, il va avoir une boucle infinie !*/
                                                                        /* On lance une exception pour l'éviter */
                                                                        if(dejaVu.contains(neededPiece))
                                                                        	throw new InfiniteLoopException() ;
                                                                        producedPieceProc.price += minPrice(neededPiece,tabs+"|\t|\t",dejaVu).price ;
                                                                        producedPieceProc.instructions += minPrice(neededPiece,tabs+"|\t|\t",dejaVu).instructions ;
                                                                }
                                                                /* On n'oublie pas d'ajouter le cout de l'operation elle-même */
                                                                producedPieceProc.price += (op.getDuration()*pricePerHour ) ;
                                                                /* Enfin on peut sortir du while(iterOpetation.hasNext) car les nom sont uniques */
                                                                break ;
                                                        }
                                                }
                                        }
                                        /* Voilà ! A ce stade du programme, on sait le prix de la piece fabriquée */
                                        /* On peut mettre à jour le minPrice */
                                        if(minProc.price==-1 || producedPieceProc.price<minProc.price) {
                                                minProc = producedPieceProc ;
                                        }      
                                }
                        }
                }
                minProc.result=label ;
                /* Avant de resortir, il faut enlever le piece en cours de dejaVu */
                dejaVu.remove(label) ;
                return minProc ;
        }
       
        /**
         * Verification des contraintes sur le workshop n'etant pas verifiees a la creation
         * @return true si ok, false si ko
         */
        public boolean checkWorkshop(){
                Iterator<Piece> it=availablePieces.iterator();
                Piece ptestee;
                /* verification des montages des pieces fabriquees */
                for (;it.hasNext();){
                        ptestee=it.next();
                        if(ptestee instanceof ProducedPiece)
                                if (!((ProducedPiece) ptestee).checkMontage())
                                        return false;
                }
                return true;
        }
       
       
}
